#include "RBMLearn.h"

#include <windows.h>

CRBMLearn::CRBMLearn(const shared_ptr<RBM> & spRBM) :
	m_spRBM(spRBM)
{
}

void CRBMLearn::Trainning(const Matrix2 & vData, double learnRate, int min_batch)
{
	int times = 0;

	Matrix2 matW = this->m_spRBM->GetWeightMatrix();
	Matrix2 matWT(matW.GetLineCount(), matW.GetColumnCount());
	Matrix2 matVB = this->m_spRBM->GetVisibleLayerBiasVector();
	Matrix2 matHB = this->m_spRBM->GetHideLayerBiasVector();

	Matrix2 matV(1, matW.GetColumnCount());
	Matrix2 matVP(1, matW.GetColumnCount());

	Matrix2 matH(1, matW.GetLineCount());
	Matrix2 matHP(1, matW.GetLineCount());

	Matrix2 matVT(1, matW.GetColumnCount());
	Matrix2 matVPT(1, matW.GetColumnCount());
	Matrix2 matLastHT(1, matW.GetLineCount());

	Matrix2 matDeltaW(matW.GetColumnCount(), matW.GetLineCount());
	Matrix2 matDeltaW1(matW.GetColumnCount(), matW.GetLineCount());
	Matrix2 matDeltaW2(matW.GetColumnCount(), matW.GetLineCount());

	Matrix2 matDeltaVB(1, matW.GetLineCount());
	Matrix2 matDeltaHB(1, matW.GetColumnCount());

	while (++times < min_batch)
	{
		matV = vData;
		Matrix2::Transpose(matW, matWT);

		Matrix2::Multiply(matW, matV, matH);
		matH = matH + matVB;
		for (int n = 0; n < matH.GetLineCount(); n++)
		{
			matH[n][0] = this->PHI(matH[n][0]);
		}

		Matrix2::Multiply(matWT, matH, matVP);
		matVP = matVP + matHB;
		for (int n = 0; n < matVP.GetLineCount(); n++)
		{
			matVP[n][0] = this->PHI(matVP[n][0]);
		}

		Matrix2::Multiply(matW, matVP, matHP);
		matHP = matHP + matVB;
		for (int n = 0; n < matH.GetLineCount(); n++)
		{
			matHP[n][0] = this->PHI(matHP[n][0]);
		}

		Matrix2::Transpose(matV, matVT);
		Matrix2::Multiply(matH, matVT, matDeltaW1);

		Matrix2::Transpose(matVP, matVPT);
		Matrix2::Multiply(matHP, matVPT, matDeltaW2);

		matDeltaW = matDeltaW1;
		matDeltaW = matDeltaW - matDeltaW2;
		matDeltaW = matDeltaW * learnRate;
		matW = matW + matDeltaW;
		
		matDeltaVB = matH;
		matDeltaVB = matDeltaVB - matHP;
		matDeltaVB = matDeltaVB * learnRate;
		matVB = matVB + matDeltaVB;

		matDeltaHB = matV;
		matDeltaHB = matDeltaHB - matVP;
		matDeltaHB = matDeltaHB * learnRate;
		matHB = matHB + matDeltaHB;
	}

	//printf("matW=");
	//matW.PrintMatrix();
	//printf("matVB=");
	//matVB.PrintMatrix();
	//printf("matHB=");
	//matHB.PrintMatrix();
	//::Sleep(100);

	this->m_spRBM->ResetRBMParameter(matW, matVB, matHB);
}

double CRBMLearn::PHI(double x)
{
	return 1.0 / (1.0 + ::exp(-1 * x));
}